package folder.model.node;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.PriorityQueue;

import folder.model.link.Link;

public class PathsNodeSource
{
	private final List< Node > nodes;
	private final List< Link > links;
	
	public static PathsNodeSource pathsNodeSource( List< Node > nodes, List< Link > links )
	{
		return new PathsNodeSource( nodes, links );
	}
	
	public PathsNodeSource( List< Node > nodes, List< Link > links )
	{
		this.nodes = nodes;
		this.links = links;
	}
	
 	public void setPathsNodeSource( Node nodeSource )
    {
        for( Node node: nodes )
        {
        	node.resetDelayToNodeSource();
        	node.resetNodePrevious();
        }
    	
        nodeSource.setDelayToNodeSource( 0. );
        PriorityQueue< Node > queue = new PriorityQueue< Node >();
      	queue.add( nodeSource );

		while ( !queue.isEmpty() )
		{
			Node nodePriority = queue.poll();
			
			for( Link linkNodePriority: nodePriority.getLinks() )
			{	
				Node nodeLinkNodePriority = linkNodePriority.getNodeA().equals( nodePriority )? linkNodePriority.getNodeB() : linkNodePriority.getNodeA();				
				
				double delayThroughNodePriority = nodePriority.getDelayToNodeSource() + linkNodePriority.getDelay();
				
				if( delayThroughNodePriority < nodeLinkNodePriority.getDelayToNodeSource() )
				{
					queue.remove( nodeLinkNodePriority );
					nodeLinkNodePriority.setDelayToNodeSource( delayThroughNodePriority );
					nodeLinkNodePriority.setNodePrevious( nodePriority );
					queue.add( nodeLinkNodePriority );
				}
			}
		}
    }
 	
 	public void putDelayToNodeTarget( Node nodeSource )
 	{
		nodeSource.resetDelayToNodeTarget();
		
		for( Node nodeTarget: nodes )
			nodeSource.putDelayToNodeTarget( nodeTarget, nodeTarget.getDelayToNodeSource() );
 	}
 	
 	public void putLinksToNodeTarget( Node nodeSource )
 	{
		nodeSource.resetLinksToNodeTarget();
		
		for( Node nodeTarget: nodes )
		{
			List< Node > nodesToNodeTarget = this.getNodesToNodeTarget( nodeTarget );
			List< Link > linksToNodeTarget = this.getLinksToNodeTarget( nodesToNodeTarget );
			nodeSource.putLinksToNodeTarget( nodeTarget, linksToNodeTarget );
		}
 	}
 	
    private List< Node > getNodesToNodeTarget( Node nodeTarget )
    {
        List< Node > nodesToNodeTarget = new ArrayList< Node >();
        
        for( Node nodeToNodeTarget = nodeTarget; nodeToNodeTarget != null; nodeToNodeTarget = nodeToNodeTarget.getNodePrevious() )
            nodesToNodeTarget.add( nodeToNodeTarget );
        
        Collections.reverse( nodesToNodeTarget );
        
        return nodesToNodeTarget;
    }
    
    private List< Link > getLinksToNodeTarget( List< Node > nodesToNodeTarget )
    {
    	List< Link > linksToNodeTarget = new ArrayList< Link >();
    	
		for( int i = 0 ; i < nodesToNodeTarget.size() - 1 ; i++ )
		{
			Node nodeAToNodeTarget = nodesToNodeTarget.get( i );
			Node nodeBToNodeTarget = nodesToNodeTarget.get( i + 1 );
			Link linkToNodeTarget = this.getLinkOfNodes( nodeAToNodeTarget, nodeBToNodeTarget );
			linksToNodeTarget.add( linkToNodeTarget );
		}
		
		return linksToNodeTarget;
    }
    
	private Link getLinkOfNodes( Node nodeA, Node nodeB )
	{
		for( Link linkNetwork: links )
			if( ( linkNetwork.getNodeA().equals( nodeA ) && linkNetwork.getNodeB().equals( nodeB ) ) || 
				( linkNetwork.getNodeA().equals( nodeB ) && linkNetwork.getNodeB().equals( nodeA ) ) )
				return linkNetwork;
		
		return null;
	}
    
}
